1   //  Copyright 2011 The Apache Software Foundation
2   //
3   //  Licensed under the Apache License, Version 2.0 (the "License");
4   //  you may not use this file except in compliance with the License.
5   //  You may obtain a copy of the License at
6   //
7   //  http://www.apache.org/licenses/LICENSE-2.0
8   //
9   //  Unless required by applicable law or agreed to in writing, software
10  //  distributed under the License is distributed on an "AS IS" BASIS,
11  //  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  //  See the License for the specific language governing permissions and
13  //  limitations under the License.
14  
15  package org.apache.tapestry5.integration.cluster;
16  
17  import com.thoughtworks.selenium.DefaultSelenium;
18  import com.thoughtworks.selenium.Selenium;
19  import org.apache.tapestry5.test.Jetty7Runner;
20  import org.apache.tapestry5.test.TapestryTestConstants;
21  import org.eclipse.jetty.server.Server;
22  import org.eclipse.jetty.server.session.JDBCSessionIdManager;
23  import org.eclipse.jetty.server.session.JDBCSessionManager;
24  import org.eclipse.jetty.server.session.SessionHandler;
25  import org.eclipse.jetty.webapp.WebAppContext;
26  import org.openqa.selenium.server.RemoteControlConfiguration;
27  import org.openqa.selenium.server.SeleniumServer;
28  import org.testng.annotations.AfterClass;
29  import org.testng.annotations.BeforeClass;
30  import org.testng.annotations.Test;
31  import org.testng.xml.XmlTest;
32  
33  import java.sql.Connection;
34  import java.sql.DriverManager;
35  import java.sql.SQLException;
36  import java.sql.Statement;
37  
38  import static org.testng.AssertJUnit.assertEquals;
39  import static org.testng.AssertJUnit.assertTrue;
40  
41  /**
42   * User: josh_canfield Date: 6/24/11
43   */
44  public class ClusterTests
45  {
46      private static final String FIREFOX_BROWSER_CMD = "*firefox";
47      private static final int SERVER_A_PORT = 9091;
48      private static final int SERVER_B_PORT = 9092;
49      private static final String SERVER_A_NAME = "server_A";
50      private static final String SERVER_B_NAME = "server_B";
51  
52      private static final String CREATE_1 = "//a[contains(text(),'create1')]";
53      private static final String VALUE_1 = "value1";
54  
55      private static final String CREATE_2 = "//a[contains(text(),'create2')]";
56      private static final String VALUE_2 = "value2";
57  
58      private static final String UPDATE_1 = "//a[contains(text(),'update1')]";
59      private static final String VALUE_3 = "value3";
60  
61      private static final String UPDATE_2 = "//a[contains(text(),'update2')]";
62      private static final String VALUE_4 = "value4";
63  
64      private static final String CLEAR = "//a[contains(text(),'Clear')]";
65  
66      Jetty7Runner serverA;
67  
68      Jetty7Runner serverB;
69  
70      SeleniumServer seleniumServer;
71      Selenium selenium;
72  
73      @BeforeClass
74      void setupServers(XmlTest xmlTest) throws Exception
75      {
76          createJettySessionsTable();
77  
78          serverA = configureClusteredJetty(SERVER_A_NAME, SERVER_A_PORT);
79          serverB = configureClusteredJetty(SERVER_B_NAME, SERVER_B_PORT);
80  
81          seleniumServer = new SeleniumServer();
82          seleniumServer.start();
83  
84          String browserStartCommand = xmlTest.getParameter(TapestryTestConstants.BROWSER_START_COMMAND_PARAMETER);
85          browserStartCommand = browserStartCommand != null ? browserStartCommand : FIREFOX_BROWSER_CMD;
86  
87          selenium = new DefaultSelenium(
88                  "localhost", RemoteControlConfiguration.DEFAULT_PORT,
89                  browserStartCommand, "http://localhost:9091/"
90          );
91          selenium.start();
92      }
93  
94      @AfterClass
95      void stopServers()
96      {
97          serverA.stop();
98          serverB.stop();
99          selenium.stop();
100         seleniumServer.stop();
101     }
102 
103     @Test
104     public void mutable_pojo_as_session_state_is_always_shared()
105     {
106         // Expect all object changes to be transferred to the other server.
107         String[][] click = {
108                 {CREATE_1, VALUE_1, VALUE_1},
109                 {CLEAR, "", ""},
110                 {CREATE_2, VALUE_2, VALUE_2},
111                 {UPDATE_1, VALUE_3, VALUE_3},
112                 {UPDATE_2, VALUE_4, VALUE_4}
113         };
114 
115         evaluate("PersistedMutablePojoDemo", click);
116     }
117 
118     @Test
119     public void immutable_session_persisted_object() throws InterruptedException
120     {
121         // expect only create links to transfer over (We've told tapestry it's immutable)
122         String[][] data = {
123                 {CREATE_1, VALUE_1, VALUE_1},
124                 {UPDATE_1, VALUE_3, VALUE_1},
125                 {CLEAR, "", ""},
126                 {CREATE_2, VALUE_2, VALUE_2},
127                 {UPDATE_2, VALUE_4, VALUE_2},
128                 {UPDATE_1, VALUE_3, VALUE_2},
129         };
130 
131         evaluate("ImmutableSessionPersistedObjectDemo", data);
132     }
133 
134     @Test
135     public void session_persisted_object_analyzer() throws InterruptedException
136     {
137         // special cased so that only UPDATE_2 marks as dirty, creates/deletes still transfer
138         String[][] data = {
139                 {CREATE_1, VALUE_1, VALUE_1}, // created, session transferred
140                 {UPDATE_1, VALUE_3, VALUE_1}, // update-1 doesn't transfer
141                 {CLEAR, "", ""},
142                 {CREATE_2, VALUE_2, VALUE_2}, // create-2, session transferred
143                 {UPDATE_2, VALUE_4, VALUE_4}, // update-2, session transferred
144                 {UPDATE_1, VALUE_3, VALUE_4}, // update-1, doesn't transfer
145         };
146 
147         evaluate("SessionPersistedObjectAnalyzerDemo", data);
148     }
149 
150     private void evaluate(String page, String[][] expect)
151     {
152         for (String[] strings : expect)
153         {
154             openOnServerA(page);
155 
156             clickAndWait(strings[0]);
157 
158             assertText("value", strings[1]);
159 
160             openOnServerB(page);
161 
162             assertText("value", strings[2]);
163         }
164 
165         clickAndWait(CLEAR);
166         assertText("value", "");
167     }
168 
169     private void openOnServerA(String page)
170     {
171         selenium.open("http://localhost:" + SERVER_A_PORT + "/" + page);
172         assertServerName(SERVER_A_NAME);
173     }
174 
175     private void openOnServerB(String page)
176     {
177         selenium.open("http://localhost:" + SERVER_B_PORT + "/" + page);
178         assertServerName(SERVER_B_NAME);
179     }
180 
181     private void assertServerName(String serverName)
182     {
183         assertTrue(selenium.isElementPresent("//h1[@id='serverName' and text()='" + serverName + "']"));
184     }
185 
186     private void assertText(String locator, String expected)
187     {
188         assertEquals(expected, selenium.getText(locator));
189     }
190 
191     private void clickAndWait(String s)
192     {
193         selenium.click(s);
194         selenium.waitForPageToLoad("5000");
195     }
196 
197     private Jetty7Runner configureClusteredJetty(String name, int port) throws Exception
198     {
199         Jetty7Runner runner = new Jetty7Runner();
200 
201         runner.configure("src/test/cluster", "", port, port + 100);
202 
203         JDBCSessionIdManager idMgr = new JDBCSessionIdManager(runner.getServer());
204         idMgr.setWorkerName(name);
205         idMgr.setDriverInfo("org.hsqldb.jdbcDriver", "jdbc:hsqldb:mem:clustertest");
206 
207         Server server = runner.getServer();
208         server.setSessionIdManager(idMgr);
209 
210         WebAppContext wac = (WebAppContext) server.getHandler();
211 
212         JDBCSessionManager jdbcMgr = new JDBCSessionManager();
213         jdbcMgr.setIdManager(server.getSessionIdManager());
214 
215         // force the session to be read from the database with no delay
216         // This is an incorrectly documented feature.
217         jdbcMgr.setSaveInterval(0);
218 
219         wac.setSessionHandler(new SessionHandler(jdbcMgr));
220         wac.getServletContext().setInitParameter("cluster.name", name);
221         runner.start();
222         return runner;
223     }
224 
225     private void createJettySessionsTable() throws ClassNotFoundException, SQLException
226     {
227         Class.forName("org.hsqldb.jdbcDriver");
228 
229         Connection c = DriverManager.getConnection("jdbc:hsqldb:mem:clustertest", "sa", "");
230         String sql = "create table JettySessions (" +
231                 "rowId varchar(60), " +
232                 "sessionId varchar(60)," +
233                 "contextPath varchar(60)," +
234                 "virtualHost varchar(60)," +
235                 "lastNode varchar(60)," +
236                 "accessTime bigint," +
237                 "lastAccessTime bigint," +
238                 "createTime bigint," +
239                 "cookieTime bigint," +
240                 "lastSavedTime bigint," +
241                 "expiryTime bigint," +
242                 "map longvarbinary" +
243                 ");";
244         Statement statement = c.createStatement();
245         statement.execute(sql);
246         statement.close();
247     }
248 }